package cn.wsalix.admin.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.anlaser.admin.BaseUser;
import cn.anlaser.page.QueryPage;
import cn.anlaser.page.QuerySpecification;
import cn.anlaser.service.impl.CommonServiceImpl;
import cn.anlaser.status.FlagEnum;
import cn.anlaser.utils.BeanMapper;
import cn.anlaser.utils.DynamicSpecifications;
import cn.anlaser.utils.SearchFilter;
import cn.anlaser.utils.SearchFilter.Operator;
import cn.wsalix.admin.entity.SysMenu;
import cn.wsalix.admin.form.MenuForm;
import cn.wsalix.admin.repository.MenuRepository;
import cn.wsalix.admin.repository.PermitRepository;
import cn.wsalix.admin.repository.RoleMenuRepository;
import cn.wsalix.admin.service.MenuService;
import cn.wsalix.admin.status.MenuEnum;
import cn.wsalix.observable.service.MenuChangeService;

@Service("menuService")
public class MenuServiceImpl extends
		CommonServiceImpl<MenuForm, MenuRepository, SysMenu> implements
		MenuService {
	@Autowired
	private MenuRepository menuRepository;

	@Autowired
	private RoleMenuRepository roleMenuRepository;
	/*
	 * @Autowired private RoleService roleService;
	 */
	@Autowired
	private PermitRepository permitRepository;

	@Override
	public MenuRepository getRepository() {
		return menuRepository;
	}

	@Override
	public String module() {
		return "/admin/menu";
	}

	@Override
	public Class<SysMenu> getEntityClazz() {
		return SysMenu.class;
	}

	@Override
	public Map<String, SearchFilter> buildByForm(MenuForm form) {
		Map<String, SearchFilter> filters = buildFilter(form);
		if (form.getCode() != null) {
			filters.put("code",
					new SearchFilter("code", Operator.EQ, form.getCode()));
		}
		if (form.getName() != null) {
			filters.put("name",
					new SearchFilter("name", Operator.EQ, form.getName()));
		}
		if (form.getMenuUrl() != null) {
			filters.put("menuUrl", new SearchFilter("menuUrl", Operator.EQ,
					form.getMenuUrl()));
		}
		if (form.getStatus() != null) {
			filters.put("status", new SearchFilter("status", Operator.EQ, form
					.getStatus().ordinal()));
		}
		if (form.isParentNull()) {
			filters.put("parent", new SearchFilter("parent.id",
					Operator.ISNULL, null));
		}
		if (form.getParentId() != null && form.getParentId() != 0) {
			filters.put("parent.id", new SearchFilter("parent.id", Operator.EQ,
					form.getParentId()));
		}

		return filters;
	}

	// @Cacheable(value = "menuCache",key="#id")
	@Override
	public SysMenu findById(Long id) {
		return super.findById(id);
	}

	@Transactional
	@Override
	public SysMenu add(MenuForm form) {
		SysMenu obj = BeanMapper.map(form, getEntityClazz());
		traversalAssign(form, obj);
		if (form.getMenuUrl() != null && !form.getMenuUrl().trim().equals("")) {
			String menuUrl = form.getMenuUrl();
			if (menuUrl.lastIndexOf("/") == -1 || menuUrl.lastIndexOf("/") == 0) {
				obj.setPattern(menuUrl);
			} else {
				String pattern = menuUrl.substring(0, menuUrl.lastIndexOf("/"));
				obj.setPattern(pattern);
			}

		}
		if (form.getParentId() != null && form.getParentId() != 0) {
			SysMenu parent = findById(form.getParentId());
			obj.setParent(parent);
			obj.setLevel(parent.getLevel() + 1);
		} else {
			obj.setLevel(1);
		}
		obj.setFlag(FlagEnum.valid);		
		return add(obj);
	}

	@Transactional
	// @CacheEvict(value = "myMenuCache", allEntries = true)
	// @CacheEvict(value = "menuCache", key = "#form.getId()")
	@Override
	public SysMenu edit(MenuForm form) {
		if (form.getMenuUrl() != null && !form.getMenuUrl().trim().equals("")) {
			String menuUrl = form.getMenuUrl();
			if (menuUrl.lastIndexOf("/") == -1 || menuUrl.lastIndexOf("/") == 0) {
				form.setPattern(menuUrl);
			} else {
				String pattern = menuUrl.substring(0, menuUrl.lastIndexOf("/"));
				form.setPattern(pattern);
			}
		}
		// upUserRoleByMenuId(form.getId());//已使用菜单不允许修改，必须先下线
		return super.edit(form);
	}

	/*
	 * private void upUserRoleByMenuId(Long menuId) { List<SysRole> roles =
	 * roleService.findRolesByMenuId(menuId); for (SysRole role : roles) {
	 * roleService.upUserRole(role.getId()); } }
	 */
	@CacheEvict(value = "menuCache", allEntries = true)
	public void reload() {
	}

	@Transactional
	@CacheEvict(value = "menuCache", key = "#id")
	// @CacheEvict(value = "myMenuCache", allEntries = true)
	@Override
	public void delete(Long id) {
		// upUserRoleByMenuId(id);
		super.delete(id);
	}

	@Transactional
	@Override
	public void delete(Iterable<SysMenu> entitys) {
		// TODO Auto-generated method stub
		super.delete(entitys);
	}

	@Override
	public List<SysMenu> findUnMenusByRoleId(Long roleId) {
		// TODO Auto-generated method stub
		return menuRepository.findUnMenusByRoleId(roleId);
	}

	@Value("${menu.level}")
	private int menuLevel;

	@Cacheable(value = "myMenuCache", key = "#userId")
	@Override
	public List<SysMenu> findMenusByUserId(Long userId) {
		/*
		 * List<SysMenu>
		 * resParentMenus=menuRepository.findParentNullByUserId(userId);
		 * List<SysMenu> resMenu=new ArrayList<SysMenu>(); for(SysMenu
		 * menuItem:resParentMenus){ List<SysMenu>
		 * resMenus=menuRepository.findByUserIdAndParentId
		 * (menuItem.getId(),userId); menuItem.setChilds(resMenus);
		 * resMenu.add(menuItem); }
		 */
		List<SysMenu> menus = roleMenuRepository.findByUserId(userId);
		List<SysMenu> resMenu = new ArrayList<SysMenu>();
		for (SysMenu menuItem : menus) {
			if (menuItem.getLevel() == menuLevel) {
				for (SysMenu menuChild : menus) {
					if (menuChild.getParent() != null) {
						if (menuChild.getParent().getId() == menuItem.getId()) {
							if (menuItem.getChilds() == null) {
								menuItem.setChilds(new ArrayList<SysMenu>());
							}
							if (!menuItem.getChilds().contains(menuChild)) {
								menuItem.getChilds().add(menuChild);
							}
						}
					}
				}
				if (!resMenu.contains(menuItem)) {
					resMenu.add(menuItem);
				}

			}
		}

		return resMenu;
	}

	@Override
	public SysMenu findByUrl(String menuUrl) {
		MenuForm form = new MenuForm();
		form.setMenuUrl(menuUrl);
		List<SysMenu> menus = findByForm(form);
		if (menus.size() != 1) {
			if (menus.size() > 1) {
				logger.error(menuUrl + " : size() > 1 [" + menus.size() + "]");
				return menus.get(0);
			}
			logger.error(menuUrl + " : size() =0 ");
			return null;
		} else {
			return menus.get(0);
		}
	}

	@Override
	public List<SysMenu> findUnsByPermitId(Long permitId) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public SysMenu findByCodeOne(String code) {
		logger.info(code);
		List<SysMenu> objs = findsByCode(code);
		if (objs.size() != 1) {
			if (objs.size() > 1) {
				logger.error(code + " : size() > 1 [" + objs.size() + "]");
				return objs.get(0);
			}
			logger.error(code + " : size() =0 ");
			return null;
		} else {
			return objs.get(0);
		}
	}

	@Override
	public SysMenu findByNameOne(String name) {
		logger.info(name);
		List<SysMenu> objs = findsByName(name);
		if (objs.size() != 1) {
			if (objs.size() > 1) {
				logger.error(name + " : size() > 1 [" + objs.size() + "]");
				return objs.get(0);
			}
			logger.error(name + " : size() =0 ");
			return null;
		} else {
			return objs.get(0);
		}
	}

	@Override
	public List<SysMenu> findsByName(String name) {
		return menuRepository.findByName(name);
	}

	@Override
	public List<SysMenu> findsByCode(String code) {
		return menuRepository.findsByCode(code);
	}

	@Override
	public List<SysMenu> findSubsById(Long parentId) {
		MenuForm form = new MenuForm();
		form.setParentId(parentId);
		form.setStatus(MenuEnum.invocation);
		return findByForm(form);
	}

	@Override
	public List<SysMenu> findsByParentId(Long parentId) {
		SysMenu parent = menuRepository.findOne(parentId);
		return parent.getSubs();
	}

	@Override
	public List<SysMenu> findStepByPattern(MenuForm menuForm) {
		List<SysMenu> menuRes = menuRepository.findByPattern(menuForm
				.getPattern());
		if (menuRes.isEmpty()) {
			return new ArrayList<SysMenu>();
		}
		SysMenu menu1 = menuRes.get(0);
		menuForm.setMenuItem(menu1);
		List<SysMenu> menus = new ArrayList<SysMenu>();
		menus.add(menu1);
		while (menu1 != null && menu1.getParent() != null) {
			menus.add(menu1.getParent());
			menu1 = menu1.getParent();
		}
		Collections.reverse(menus);
		return menus;
	}

	@Transactional
	@Override
	public void update(Observable o, Object arg) {
		if (o instanceof MenuChangeService) {
			MenuChangeService menuChangeService = (MenuChangeService) o;
			if (arg instanceof MenuForm) {
				MenuForm menuForm = (MenuForm) arg;
				SysMenu menu = null;
				switch (menuForm.getOperate()) {
				case add:
					menu = add(menuForm);
					menuChangeService.setMenu(menu);
					break;
				case del:
					menu = findById(menuForm.getId());
					menuChangeService.setMenu(menu);
					delete(menu);
					break;
				case edit:
					menu = edit(menuForm);
					menuChangeService.setMenu(menu);
					break;
				default:
					break;
				}
			}
		}
	}

	@Transactional
	@Override
	public SysMenu add(MenuForm form, SysMenu parent) {
		SysMenu obj = BeanMapper.map(form, getEntityClazz());
		traversalAssign(form, obj);
		if (parent != null && parent.getId() != null) {
			obj.setParent(parent);
			obj.setLevel(parent.getLevel() + 1);
		} else {
			obj.setLevel(1);
		}
		obj.setCreatedDate(new Date());// DateTime.now());
		BaseUser user = userUtils.getUser();
		if (obj.getFlag() == null) {// 默认数据，全部有效。新增商品、广告、文章，默认无效，新建时设置
			obj.setFlag(FlagEnum.valid);
		}
		if (user != null) {
			obj.setCreatedBy(user.getId());
		}
		return super.add(obj);
	}

	@Override
	public QueryPage<SysMenu> findParentTop(MenuForm form, Pageable pageable) {
		Map<String, SearchFilter> filters = null;
		if (form == null) {
			Map<String, Object> searchParams = new HashMap<String, Object>();
			filters = SearchFilter.parse(searchParams);
		} else {
			filters = buildByForm(form);
		}
		filters.put("parent", new SearchFilter("parent.id", Operator.ISNULL,
				null));

		QuerySpecification<SysMenu> spec = DynamicSpecifications
				.bySearchFilter(filters.values(), getEntityClazz());

		// Sort sort = new Sort(Sort.Direction.ASC, "serNo");
		QueryPage<SysMenu> resPage = new QueryPage<SysMenu>();
		resPage.setQueryLine(spec.getQueryLine() + "&size="
				+ pageable.getPageSize() + "&orders=id" + "&direction="
				+ Sort.Direction.ASC);
		resPage.setPage(getRepository().findAll(
				spec,
				new PageRequest(pageable.getPageNumber(), pageable
						.getPageSize(), new Sort(Sort.Direction.ASC,
						new String[] { "id" }))));
		return resPage;
	}

	@Override
	public QueryPage<SysMenu> findByParent(MenuForm form, Pageable pageable) {
		Map<String, SearchFilter> filters = null;
		if (form == null) {
			Map<String, Object> searchParams = new HashMap<String, Object>();
			filters = SearchFilter.parse(searchParams);
		} else {
			filters = buildByForm(form);
		}
		if (form.getParentId() != null && form.getParentId() != 0) {
			filters.put("parent", new SearchFilter("parent.id", Operator.EQ,
					form.getParentId()));
		}
		QuerySpecification<SysMenu> spec = DynamicSpecifications
				.bySearchFilter(filters.values(), getEntityClazz());

		QueryPage<SysMenu> resPage = new QueryPage<SysMenu>();
		if (form.getOrders() == null||form.getOrders().equals("")) {
			form.setOrders("id");
		}
		resPage.setQueryLine(spec.getQueryLine() + "&size="
				+ pageable.getPageSize() + "&orders=" + form.getOrders()
				+ "&direction=" + form.getDirection());
		resPage.setPage(getRepository().findAll(
				spec,
				new PageRequest(pageable.getPageNumber(), pageable
						.getPageSize(), new Sort(form.getDirection(),
						new String[] { form.getOrders() }))));
		return resPage;
	}
}
